﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Threading;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.IO;
using System.Xml.Serialization;

namespace DragDraw.Util
{
    public class OnUpdateSessionEventArgs : EventArgs
    {
        public SessionData Result;
    }
    public class SessionData
    {
        public string UserID;
        public int WidgetID;
        public string SessionName;
        public string Value;
    }
    public class UserServiceDuplexClient
    {
        public static readonly string SERVICE_ADDRESS = "http://localhost:49393/UserDuplexService.svc";
        private static UserServiceDuplexClient m_instance;
        static UserServiceDuplexClient()
        {
            m_instance = new UserServiceDuplexClient();
        }
        public static UserServiceDuplexClient Instance
        {
            get { return m_instance; }
        }

        private SynchronizationContext m_uiThread;
        private IDuplexSessionChannel m_channel;

        public bool Ready { get; set; }
        public event EventHandler<OnUpdateSessionEventArgs> OnReceive;

        public UserServiceDuplexClient()
        {
            Ready = false;
            m_uiThread = SynchronizationContext.Current;

            Start();
        }

        public void SendJoinSession(string userID, int widgetID, string sessionName)
        {
            SessionData s = new SessionData()
            {
                UserID = userID,
                WidgetID = widgetID,
                SessionName = sessionName
            };
            StringWriter sw = new StringWriter();
            XmlSerializer xs = new XmlSerializer(typeof(SessionData));
            xs.Serialize(sw, s);
            Send("Silverlight/IUserDuplexService/JoinSession", sw.ToString());
        }

        public void SendUpdateSession(SessionData sessionData)
        {
            StringWriter sw = new StringWriter();
            XmlSerializer xs = new XmlSerializer(typeof(SessionData));
            xs.Serialize(sw, sessionData);
            Send("Silverlight/IUserDuplexService/JoinSession", sw.ToString());
        }

        public void Send(string action, string actionData)
        {
            // The channel is now open. Send a message
            Message message = Message.CreateMessage(m_channel.GetProperty<MessageVersion>(), action, actionData);
            IAsyncResult resultChannel = m_channel.BeginSend(message, new AsyncCallback(OnSend), m_channel);
            if (resultChannel.CompletedSynchronously)
            {
                CompleteOnSend(resultChannel);
            }

            // Also start the receive loop to listen for callbacks from the service.
            ReceiveLoop(m_channel);
        }

        void HandleReceive(object receiveString)
        {
            OnUpdateSessionEventArgs e = new OnUpdateSessionEventArgs();
            StringReader sr = new StringReader((string)receiveString);
            XmlSerializer xs = new XmlSerializer(typeof(SessionData));
            e.Result = (SessionData)xs.Deserialize(sr);
            OnReceive(this, e);
        }

        void Start()
        {
            // Instantiate the binding and set the time-outs
            PollingDuplexHttpBinding binding = new PollingDuplexHttpBinding()
            {
                PollTimeout = TimeSpan.FromSeconds(10),
                InactivityTimeout = TimeSpan.FromMinutes(1)
            };

            // Instantiate and open channel factory from binding
            IChannelFactory<IDuplexSessionChannel> factory =
                binding.BuildChannelFactory<IDuplexSessionChannel>(new BindingParameterCollection());

            IAsyncResult factoryOpenResult =
                factory.BeginOpen(new AsyncCallback(OnOpenCompleteFactory), factory);
            if (factoryOpenResult.CompletedSynchronously)
            {
                CompleteOpenFactory(factoryOpenResult);
            }
        }

        void OnOpenCompleteFactory(IAsyncResult result)
        {
            if (result.CompletedSynchronously)
                return;
            else
                CompleteOpenFactory(result);
        }

        void CompleteOpenFactory(IAsyncResult result)
        {
            IChannelFactory<IDuplexSessionChannel> factory =
                (IChannelFactory<IDuplexSessionChannel>)result.AsyncState;

            factory.EndOpen(result);

            // The factory is now open. Create and open a channel from the channel factory.
            IDuplexSessionChannel channel =
                factory.CreateChannel(new EndpointAddress(SERVICE_ADDRESS));

            IAsyncResult channelOpenResult =
                channel.BeginOpen(new AsyncCallback(OnOpenCompleteChannel), channel);
            if (channelOpenResult.CompletedSynchronously)
            {
                CompleteOpenChannel(channelOpenResult);
            }
        }

        void OnOpenCompleteChannel(IAsyncResult result)
        {
            if (result.CompletedSynchronously)
                return;
            else
                CompleteOpenChannel(result);
        }

        void CompleteOpenChannel(IAsyncResult result)
        {
            m_channel = (IDuplexSessionChannel)result.AsyncState;

            m_channel.EndOpen(result);

            Ready = true;

            //---------------------------------------------------------------
            // Channel is now open. enter receive loop
            //ReceiveLoop(m_channel);
        }

        void OnSend(IAsyncResult result)
        {
            if (result.CompletedSynchronously)
                return;
            else
                CompleteOnSend(result);
        }

        void CompleteOnSend(IAsyncResult result)
        {
            IDuplexSessionChannel channel = (IDuplexSessionChannel)result.AsyncState;
            channel.EndSend(result);
        }

        void ReceiveLoop(IDuplexSessionChannel channel)
        {
            // Start listening for callbacks.
            IAsyncResult result = channel.BeginReceive(new AsyncCallback(OnReceiveComplete), channel);
            if (result.CompletedSynchronously) CompleteReceive(result);
        }

        void OnReceiveComplete(IAsyncResult result)
        {
            if (result.CompletedSynchronously)
                return;
            else
                CompleteReceive(result);
        }

        void CompleteReceive(IAsyncResult result)
        {
            //A callback was received so process data
            IDuplexSessionChannel channel = (IDuplexSessionChannel)result.AsyncState;

            try
            {
                Message receivedMessage = channel.EndReceive(result);

                // Show the service response in the UI.
                if (receivedMessage != null)
                {
                    string text = receivedMessage.GetBody<string>();
                    m_uiThread.Post(this.HandleReceive, text);
                    ReceiveLoop(channel);
                }
                else
                {
                }
            }
            catch (CommunicationObjectFaultedException exp)
            {
                m_uiThread.Post(delegate(object msg) { System.Windows.Browser.HtmlPage.Window.Alert(msg.ToString()); }, exp.Message);
            }
        }

        void OnCloseChannel(IAsyncResult result)
        {
            if (result.CompletedSynchronously)
                return;
            else
                CompleteCloseChannel(result);
        }

        void CompleteCloseChannel(IAsyncResult result)
        {
            IDuplexSessionChannel channel = (IDuplexSessionChannel)result.AsyncState;
            channel.EndClose(result);
        }
    }
}
